package com.sap.sap_mangers.service.Impl;

import com.sap.sap_mangers.Vo.BmsVo.BmsBorrowVo;
import com.sap.sap_mangers.Vo.BmsVo.BmsCategoryWithBookNumberVo;
import com.sap.sap_mangers.Vo.BmsVo.BmsManagerVo;
import com.sap.sap_mangers.Vo.BmsVo.BmsUserForTimeNoReturningVo;
import com.sap.sap_mangers.Vo.UserVo.UserMessage;
import com.sap.sap_mangers.dto.user.NoticeDto;
import com.sap.sap_mangers.email.IMailService;
import com.sap.sap_mangers.entity.Bms.BorrowNumberInEachMonth;
import com.sap.sap_mangers.mapper.BmsMapper;
import com.sap.sap_mangers.mapper.UserMapper;
import com.sap.sap_mangers.pojo.SapBmsBook;
import com.sap.sap_mangers.pojo.SapBmsBorrow;
import com.sap.sap_mangers.pojo.SapBmsManagerSimpleInfo;
import com.sap.sap_mangers.pojo.User;
import com.sap.sap_mangers.service.BmsService;
import com.sap.sap_mangers.service.CacheService;
import com.sap.sap_mangers.service.UserService;
import com.sap.sap_mangers.until.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ZhouKai
 * 2022/8/8 21:17
 */
@Service
public class BmsServiceImpl implements BmsService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    BmsMapper bmsMapper;
    @Resource
    UserMapper userMapper;
    @Resource
    IMailService iMailService;
    @Resource
    UserService userService;
    @Autowired
    RedisCache redisCache;

    @Autowired
    CacheService cacheService;

    @Override
    public void addBmsCategory(String categoryName) {
        bmsMapper.addBmsCategory(categoryName);
    }

    @Override
    public boolean updateBmsCategory(String oldCategoryName, String newCategoryName) {
        //新名称与原名称相同 不执行
        if (oldCategoryName.equals(newCategoryName)) {
            return false;
        }
        //查询原名称是否存在
        Integer oldCategoryId = bmsMapper.getBmsCategoryId(oldCategoryName);
        if (oldCategoryId != null) {
            //查找是否存在与新类别名称相同的类别名称
            Integer bmsCategoryId = bmsMapper.getBmsCategoryId(newCategoryName);
            if (bmsCategoryId == null) {
                //原名称存在 并且 与新类别名称相同的类别名称不存在 -> 执行修改，返回true
                bmsMapper.updateBmsCategory(oldCategoryName, newCategoryName);
                return true;
            }
        }
        return false;
    }

    @Override
    public List<String> getAllCategory() {
        return bmsMapper.getAllCategory();
    }

    @Override
    public void newBookIntoToBookTable(SapBmsBook sapBmsBook) {
        synchronized (this) {
            bmsMapper.newBookInsertToBookTable(sapBmsBook);
            sapBmsBook.setBmsBookId(bmsMapper.selectMaxId());
        }
    }

    @Override
    public SimpleJson addBookInsertToBmsClassification(SapBmsBook sapBmsBook) {
        List<String> allCategory = bmsMapper.getAllCategory();
        if (!allCategory.containsAll(sapBmsBook.getBmsCategory())) {
            throw new RuntimeException("未知名书籍类型，添加失败");
        }
        Integer bmsBookId = sapBmsBook.getBmsBookId();
        for (String bmsCategory : sapBmsBook.getBmsCategory()) {
            Integer bmsCategoryId = bmsMapper.getBmsCategoryId(bmsCategory);
            bmsMapper.addBookInsertToBmsClassification(bmsBookId, bmsCategoryId);
        }
        sapBmsBook.setBmsBookState("闲置");
        sapBmsBook.setBmsBookWarehousingTime(bmsMapper.getCreateTimeByBookId(bmsBookId));
        return new SimpleJson(0, "书籍添加成功", sapBmsBook);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SimpleJson addBookInsertToBookTableAndBmsClassification(SapBmsBook sapBmsBook) {
        newBookIntoToBookTable(sapBmsBook);
        return addBookInsertToBmsClassification(sapBmsBook);
    }

    @Override
    public SimpleJson getAllBookInformation() {
        List<SapBmsBook> allBookInformation = bmsMapper.getAllBookInformation();
        if (allBookInformation == null || allBookInformation.size() == 0) {
            return new SimpleJson(1, "当前书库无书");
        }
        for (SapBmsBook sapBmsBook : allBookInformation) {
            Integer bmsBookId = sapBmsBook.getBmsBookId();
            sapBmsBook.setBmsCategory(bmsMapper.getCateGoryByBookId(bmsBookId));
        }
        return new SimpleJson(0, "书籍信息获取成功", allBookInformation);
    }

    @Override
    public SimpleJson getBookInformation(String classification) {
        if (classification == null) {
            return new SimpleJson(1, "参数为空");
        }
        List<SapBmsBook> books = bmsMapper.getBookInformationByClass(classification);
        for (SapBmsBook sapBmsBook : books) {
            Integer bmsBookId = sapBmsBook.getBmsBookId();
            sapBmsBook.setBmsCategory(bmsMapper.getCateGoryByBookId(bmsBookId));
        }
        return new SimpleJson(0, "操作成功", books);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void fillBmsManagerTable() {
        //清空当前管理表
        bmsMapper.clearBmsManagerTable();
        //将所有部长及以上职务的成员的学号插入管理表 (默认情况不是管理员，只是默认管理员)
        List<String> allMinistersUsername = userMapper.getAllMinistersUsername();
        for (String username : allMinistersUsername) {
            bmsMapper.addBmsManager(username);
        }
        //单独获取会长学号，将会长设为管理员
        String presidentUserName = userMapper.getPresidentUserName();
        bmsMapper.updateBmsManager(presidentUserName, true);
    }


    @Override
    public List<BmsManagerVo> getBmsManagerVos() {
        return bmsMapper.getBmsManagerVos();
    }

    @Override
    public SimpleJson setTheManagerToBeResponsible(String username) {
        //查询当前是否负责
        Boolean responsible = bmsMapper.getResponsible(username);
        if (responsible == null) {
            return new SimpleJson(2, "未在管理表中查询到该成员信息");
        }
        if (responsible) {
            //当前已是正式管理员
            return new SimpleJson(1, "该成员原本就是正式管理员");
        } else {
            bmsMapper.updateBmsManager(username, true);
            return new SimpleJson(0, "设置正式管理员成功");
        }
    }

    @Override
    public SimpleJson setTheManagerTeBeNotResponsible(String username) {
        Integer responsibleManagerNumber = bmsMapper.getResponsibleManagerNumber();
        if (responsibleManagerNumber <= 1) {
            return new SimpleJson(3, "撤销失败，请保证至少有一位正式管理员");
        }
        //查询当前是否负责
        Boolean responsible = bmsMapper.getResponsible(username);
        if (responsible == null) {
            return new SimpleJson(2, "未在管理表中查询到该成员信息");
        }
        if (responsible) {
            //当前已是正式管理员
            bmsMapper.updateBmsManager(username, false);
            return new SimpleJson(0, "撤销管理职务成功");
        } else {
            return new SimpleJson(1, "该成员原本就不是正式管理员");
        }
    }

    @Override
    public List<BmsManagerVo> getBmsResponsibleManagerVos() {
        return bmsMapper.getBmsResponsibleManagerVos();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SimpleJson updateBookInformation(SapBmsBook sapBmsBook) {
        if (sapBmsBook.getBmsBookId() == null ||
                (bmsMapper.getBookInformationByBookId(sapBmsBook.getBmsBookId())) == null) {
            return new SimpleJson(1, "错误的书籍编号操作，修改失败");
        }
        List<String> bmsCategory = sapBmsBook.getBmsCategory();
        if (bmsCategory == null || bmsCategory.size() == 0) {
            return new SimpleJson(1, "书籍类型不允许为null，修改失败");
        }
        if (!"闲置".equals(sapBmsBook.getBmsBookState())
                && !"损坏或遗失".equals(sapBmsBook.getBmsBookState()) && !"已出库".equals(sapBmsBook.getBmsBookState())) {
            return new SimpleJson(1, "错误的书籍状态，修改失败");
        }
        if (!(sapBmsBook.getBmsBookState() == null && sapBmsBook.getBmsBookIntroduction() == null && sapBmsBook.getBmsBookPrice() == null)) {
            bmsMapper.updateBookInformationInBookTable(sapBmsBook);
        }
        List<String> allCategory = bmsMapper.getAllCategory();
        if (!allCategory.containsAll(sapBmsBook.getBmsCategory())) {
            return new SimpleJson(1, "错误的书籍类型，修改失败");
        }

        bmsMapper.deleteBookClassification(sapBmsBook.getBmsBookId());
        Integer bmsBookId = sapBmsBook.getBmsBookId();
        for (String category : bmsCategory) {
            Integer bmsCategoryId = bmsMapper.getBmsCategoryId(category);
            bmsMapper.addBookInsertToBmsClassification(bmsBookId, bmsCategoryId);
        }
        SapBmsBook bookInformationByBookId = bmsMapper.getBookInformationByBookId(bmsBookId);
        bookInformationByBookId.setBmsCategory(bmsCategory);
        return new SimpleJson(0, "修改成功", bookInformationByBookId);
    }

    @Override
    public synchronized SimpleJson applyBorrow(String username, SapBmsBorrow sapBmsBorrow) {
        Integer bookId = sapBmsBorrow.getBmsBookId();
        //当前是否闲置状态
        SapBmsBook book = bmsMapper.getBookInfo(bookId);
        if (book == null) {
            return new SimpleJson(6, "未查找到书籍");
        }
        if (!"闲置".equals(book.getBmsBookState())) {
            return new SimpleJson(1, "书籍已不在闲置状态");
        }
        //检测预计归还时间是否合理 (必须比当前时间大一天以上)
        Timestamp expectedReturnTime = sapBmsBorrow.getBmsBorrowExpectedReturnTime();
        long nowTime = System.currentTimeMillis();
        //时间长度 (单位：天)
        long timeLength = (expectedReturnTime.getTime() - nowTime) / 86400000;
        if (timeLength <= 7) {
            return new SimpleJson(2, "预计归还时间不能少于7天");
        }
        if (timeLength > 365) {
            return new SimpleJson(3, "预计归还时间不能大于365天");
        }
        //判断是否是重复的申请 (判断是否存在书籍编号和学号相同并且状态是‘申请中’的借阅记录)
        if (bmsMapper.isThereSameBorrowRecord(bookId, username) != null) {
            return new SimpleJson(4, "请勿重复申请");
        }
        //插入借阅信息
        bmsMapper.addBmsBorrowRecord(sapBmsBorrow);

        //发送邮件
        //借阅人姓名
        String name = userMapper.getUserMessage(username).getRealName();
        //所有正式管理员
        List<BmsManagerVo> responsibleManagerVos = bmsMapper.getBmsResponsibleManagerVos();
        //邮件内容
        String email = EmailUntil.emailForApprovalBook(username, name, book.getBmsBookName(), bookId);
        //站内通知内容
        String notice = "[借阅申请]\n  学号为" + username + "的同学:" +
                name + "在" + new Timestamp(nowTime) +
                "申请借阅 编号为" + bookId + "的《" + book.getBmsBookName() + "》，请及时查看是否被处理";
        //计数 - 邮件发送失败的次数
        int sendFalseCnt = 0;
        for (BmsManagerVo managerVo : responsibleManagerVos) {
            try {
                //发送用户通知
                userService.sendNotice(new NoticeDto(managerVo.getUsername(), notice, null));
                //发送邮件
                iMailService.sendHtmlMail(managerVo.getQq() + "@qq.com", "图书借阅申请", email);
            } catch (RuntimeException e) {
                //发送邮件失败，计数+1
                sendFalseCnt++;
            }
        }
        //当 邮件发送失败的次数 等于 正式管理员人数 (全部发送失败) -> 尝试向会长单独发送一次异常提示
        if (sendFalseCnt == responsibleManagerVos.size()) {
            trySendEmailToPresidentUnderAbnormalConditions("在进行借阅图书操作时", responsibleManagerVos.size());
            return new SimpleJson(5, "向管理员发送邮件提醒失败，请联系部长/会长处理");
        }
        return new SimpleJson(0, "操作成功，已向管理员发送邮件提醒");
    }

    @Override
    public synchronized SimpleJson applyReturn(String username, Integer borrowId, Boolean borrowFlag) {
        //获取对应的借阅记录
        SapBmsBorrow sapBmsBorrow = bmsMapper.getBorrowById(borrowId);
        if (sapBmsBorrow == null) {
            return new SimpleJson(1, "未查找到对应借阅记录");
        }
        //判断是否为本人的借阅记录
        if (!username.equals(sapBmsBorrow.getBmsBorrowUserName())) {
            return new SimpleJson(2, "无法操作他人的借阅记录");
        }
        //判断是否重复申请 (判断状态是否已经是 ‘申请归还’ 或 ‘申请赔偿’)
        String nowState = sapBmsBorrow.getBmsBorrowState();
        if ("申请归还".equals(nowState) || "申请赔偿".equals(nowState)) {
            return new SimpleJson(4, "请勿重复申请，如果申请类型出错联系部长/会长");
        }
        //判断当前是否在借阅中/超时未归还
        if (!"借阅中".equals(nowState) && !"超时未归还".equals(nowState)) {
            return new SimpleJson(3, "当前不在借阅状态，有异常请联系部长/会长");
        }
        //按申请类型修改借阅状态 和 生成邮件和通知的内容
        String name = userMapper.getUserMessage(username).getRealName();
        Integer bookId = sapBmsBorrow.getBmsBookId();
        sapBmsBorrow.setBmsBookName(bmsMapper.getBookNameById(bookId));
        String bookName = sapBmsBorrow.getBmsBookName();
        String subject;
        String email;
        String notice;
        if (borrowFlag) {
            //申请归还  借阅状态：借阅中->申请归还
            bmsMapper.updateBorrowState(borrowId, "申请归还");
            subject = "图书归还申请";
            email = EmailUntil.emailForReturnBook(username, name, bookName, bookId);
            notice = "[归还申请]\n  学号为" + username + "的同学:" +
                    name + "在" + new Timestamp(System.currentTimeMillis()) +
                    "申请归还 编号为" + bookId + "的《" + bookName + "》，请在确保归还后通过审批";
        } else {
            //申请赔偿  借阅状态：借阅中->申请赔偿
            bmsMapper.updateBorrowState(borrowId, "申请赔偿");
            subject = "图书赔偿申请";
            email = EmailUntil.emailForPayForBook(username, name, bookName, bookId);
            notice = "[赔偿申请]\n  学号为" + username + "的同学:" +
                    name + "因各方面原因使编号为" + bookId + "的《" + bookName + "》损坏或遗失，现于 " + new Timestamp(System.currentTimeMillis()) +
                    " 提交了赔偿申请 " + "，请在通过审批后与其联系处理";
        }

        //发送邮件和通知
        List<BmsManagerVo> responsibleManagerVos = bmsMapper.getBmsResponsibleManagerVos();
        if (sendEmailAndNoticeToResponsibleManagers(responsibleManagerVos, notice, subject, email)) {
            trySendEmailToPresidentUnderAbnormalConditions("在进行申请归还/赔偿图书操作时", responsibleManagerVos.size());
            return new SimpleJson(5, "向管理员发送邮件提醒失败，请联系部长/会长处理");
        }
        return new SimpleJson(0, "操作成功，已向管理员发送邮件提醒");
    }

    @Override
    public SimpleJson getUserBorrowInformation() {
        List<BmsBorrowVo> userBorrowInformation = bmsMapper.getUserBorrowInformation(JwtUtil.getUserForToken());
        if (userBorrowInformation == null || userBorrowInformation.size() == 0) {
            return new SimpleJson(1, "您的借阅信息为null");
        }
        return new SimpleJson(0, "查询成功", userBorrowInformation);
    }

    @Override
    public SimpleJson getAllBorrowInformation() {
        List<BmsBorrowVo> allBorrowInformation = bmsMapper.getAllBorrowInformation();
        if (allBorrowInformation == null || allBorrowInformation.size() == 0) {
            return new SimpleJson(1, "当前借阅信息为null");
        }
        return new SimpleJson(0, "查询成功", allBorrowInformation);
    }

    @Override
    public void updateAllBookTimeoutNoReturn() {
        bmsMapper.updateAllBookTimeoutNoReturn();
    }

    @Override
    public List<BmsBorrowVo> getRecordOfBorrowingApplication() {
        return bmsMapper.getRecordOfBorrowingApplication();
    }

    @Override
    public List<BmsBorrowVo> getRecordOfReturningApplication() {
        return bmsMapper.getRecordOfReturningApplication();
    }

    @Override
    public List<BmsUserForTimeNoReturningVo> getAllUserMessageForBookTimeoutNoReturn() {
        return bmsMapper.getAllUserMessageForBookTimeoutNoReturn();
    }

    @Override
    public List<SapBmsManagerSimpleInfo> getSapBmsManagerSimpleInfos() {
        return bmsMapper.getSapBmsManagerSimpleInfos();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized SimpleJson checkBorrowInformation(BmsBorrowVo bmsBorrowVo) {
        String managerId = JwtUtil.getUserForToken();
        Boolean isManager = bmsMapper.getResponsible(managerId);
        if (!"借阅中".equals(bmsBorrowVo.getBmsBorrowState()) && !"申请失败".equals(bmsBorrowVo.getBmsBorrowState())) {
            return new SimpleJson(1, "设置了错误的书籍状态，审批失败");
        }
        if (isManager == null || !isManager) {
            return new SimpleJson(1, "权限不够，审批失败");
        }
        BmsBorrowVo borrowInfo = bmsMapper.getBorrowInfoByBmsBorrowId(bmsBorrowVo.getBmsBorrowId());
        if (borrowInfo == null) {
            return new SimpleJson(1, "查无记录，审批失败");
        }
        if (!"申请中".equals(borrowInfo.getBmsBorrowState())) {
            return new SimpleJson(1, "该申请已被操作，审批过期");
        }
        if ("申请失败".equals(bmsBorrowVo.getBmsBorrowState())) {
            if (!StringUtils.hasText(bmsBorrowVo.getMessage())) {
                return new SimpleJson(2, "请给出有效的申请失败原因");
            }
        }

        bmsBorrowVo.setBmsBorrowOperator(managerId);
        bmsBorrowVo.setBmsBorrowOperatorName(cacheService.getRealNameCache(cacheService.getRealNameCacheMap(), managerId));

        bmsMapper.updateBorrowStateByOperator(bmsBorrowVo);

        borrowInfo = bmsMapper.getBorrowInfoByBmsBorrowId(bmsBorrowVo.getBmsBorrowId());
        UserMessage managerMessage = userMapper.getUserMessage(borrowInfo.getBmsBorrowOperator());
        UserMessage userMessage = userMapper.getUserMessage(borrowInfo.getBmsBorrowUserName());
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
        String message = bmsBorrowVo.getMessage();
        if ("申请失败".equals(borrowInfo.getBmsBorrowState())) {
            String contentToApplicant = EmailUntil.sendEmailToUserForApplyFail(
                    message, borrowInfo.getBmsBorrowUserName(),
                    borrowInfo.getRealName(), borrowInfo.getBmsBookName(),
                    simpleDateFormat.format(borrowInfo.getBmsBorrowApplicationTime()), borrowInfo.getBmsBorrowOperator(),
                    borrowInfo.getBmsBorrowOperatorName(), managerMessage.getQq(), managerMessage.getPhone());
            iMailService.sendHtmlMail(userMessage.getQq() + "@qq.com", "书籍申请失败", contentToApplicant);
            String notice = "您借阅《" + borrowInfo.getBmsBookName() + "》的申请已被管理员" + managerMessage.getRealName() + "拒绝。详细理由：" + message;
            userService.sendNoticeAuto(new NoticeDto(userMessage.getUsername(), notice, null));
        } else {
            //修改book表中的书籍状态
            bmsMapper.updateBorrowState(borrowInfo.getBmsBorrowId(), borrowInfo.getBmsBorrowState());
            bmsMapper.updateBookStates(borrowInfo.getBmsBookId(), borrowInfo.getBmsBorrowState());
            String contentToApplicant = EmailUntil.sendEmailToUserForApprovalPass(
                    message, borrowInfo.getBmsBorrowUserName(),
                    borrowInfo.getRealName(), borrowInfo.getBmsBookName(), borrowInfo.getBmsBookId(),
                    simpleDateFormat.format(borrowInfo.getBmsBorrowApplicationTime()), borrowInfo.getBmsBorrowOperator(),
                    borrowInfo.getBmsBorrowOperatorName(), managerMessage.getQq(), managerMessage.getPhone());
            iMailService.sendHtmlMail(userMessage.getQq() + "@qq.com", "书籍申请成功", contentToApplicant);
            String notice = "您借阅《" + borrowInfo.getBmsBookName() + "》的申请已被管理员" + managerMessage.getRealName() + "批准";
            userService.sendNoticeAuto(new NoticeDto(userMessage.getUsername(), notice, null));
        }
        String contentToOtherManagers = EmailUntil.sendEmailToOtherManagerForApprovalPass(
                borrowInfo.getBmsBorrowUserName(),
                borrowInfo.getRealName(), borrowInfo.getBmsBookName(),
                simpleDateFormat.format(borrowInfo.getBmsBorrowApplicationTime()),
                borrowInfo.getBmsBorrowOperator(), borrowInfo.getBmsBorrowOperatorName());

        List<SapBmsManagerSimpleInfo> sapBmsManagerSimpleInfos = bmsMapper.getSapBmsManagerSimpleInfos();
        for (SapBmsManagerSimpleInfo sapBmsManagerSimpleInfo : sapBmsManagerSimpleInfos) {
            if (!sapBmsManagerSimpleInfo.getManagerUsername().equals(borrowInfo.getBmsBorrowOperator())) {
                iMailService.sendHtmlMail(sapBmsManagerSimpleInfo.getManagerQq() + "@qq.com", "一条书籍申请已被其他管理员审阅", contentToOtherManagers);
                String notice = "来自" + userMessage.getUsername() + " " +
                        userMessage.getRealName() + "借阅《" + borrowInfo.getBmsBookName() + "》的批阅已被管理员" + managerMessage.getRealName() + "处理，请勿重复操作";
                System.out.println("notice = " + notice);
                userService.sendNoticeAuto(new NoticeDto(sapBmsManagerSimpleInfo.getManagerUsername(), notice, null));
            }
        }
        return new SimpleJson(0, "批阅成功");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized SimpleJson modifyBorrowStateManually(Integer borrowId, String state) {
        //操作人学号
        String userForToken = JwtUtil.getUserForToken();
        if (bmsMapper.getManagerUsernameByUsername(userForToken) == null) {
            return new SimpleJson(1, "权限不足");
        }
        if (borrowId == null || state == null) {
            return new SimpleJson(2, "参数不完整");
        }
        if (!"申请中".equals(state) && !"申请失败".equals(state) && !"借阅中".equals(state) && !"申请归还".equals(state)
                && !"申请赔偿".equals(state) && !"已归还".equals(state) && !"损坏或遗失".equals(state)) {
            return new SimpleJson(3, "不存在此借阅状态或不允许直接修改为此借阅状态");
        }

        SapBmsBorrow borrow = bmsMapper.getBorrowById(borrowId);
        if (borrow == null) {
            return new SimpleJson(4, "未查找到对应的借阅信息");
        }
        SapBmsBook book = bmsMapper.getBookInfo(borrow.getBmsBookId());
        //在修改借阅状态时会同时修改书籍状态
        //其他状态->损坏或遗失                书籍状态->损坏或遗失
        //其他状态->申请中/申请失败/已归还     书籍状态->闲置
        //其他状态->借阅中/申请归还/申请赔偿   书籍状态->借阅中
        bmsMapper.updateBorrowState(borrowId, state);
        if ("损坏或遗失".equals(state)) {
            book.setBmsBookState("损坏或遗失");
        } else if ("申请中".equals(state) || "申请失败".equals(state) || "已归还".equals(state)) {
            book.setBmsBookState("闲置");
        } else {
            book.setBmsBookState("借阅中");
        }
        bmsMapper.updateBookInformationInBookTable(book);
        return new SimpleJson(0, "操作成功");
    }

    @Override
    public SimpleJson checkBookReturnInformation(BmsBorrowVo bmsBorrowVo) {
        //得到 当前操作用户的 学号
        String managerId = JwtUtil.getUserForToken();

        //查看是否为是管理员
        Boolean isManager = bmsMapper.getResponsible(managerId);
        if (isManager == null || !isManager) {
            return new SimpleJson(1, "权限不够，审批失败");
        }

        //查看是否有该条借阅记录 --》 从借阅表中拿到该条借阅信息 borrowInfo
        BmsBorrowVo borrowInfo = bmsMapper.getBorrowInfoByBmsBorrowId(bmsBorrowVo.getBmsBorrowId());

        if (borrowInfo == null) {
            return new SimpleJson(1, "查无记录，审批失败");
        }

        bmsBorrowVo.setBmsBorrowOperator(managerId);
        bmsBorrowVo.setBmsBorrowOperatorName(cacheService.getRealNameCache(cacheService.getRealNameCacheMap(), managerId));
        SapBmsBook sapBmsBook = new SapBmsBook();
        sapBmsBook.setBmsBookId(borrowInfo.getBmsBookId());
        if ("申请归还".equals(borrowInfo.getBmsBorrowState())) {
            bmsBorrowVo.setBmsBorrowState("已归还");
            bmsBorrowVo.setBmsBorrowActualReturnTime(new Timestamp(System.currentTimeMillis()));
            sapBmsBook.setBmsBookState("闲置");
        } else if ("申请赔偿".equals(borrowInfo.getBmsBorrowState())) {
            bmsBorrowVo.setBmsBorrowState("损坏或遗失");
            sapBmsBook.setBmsBookState("损坏或遗失");
        } else {
            return new SimpleJson(1, "该申请已被操作，审批过期");
        }
        //修改book表
        bmsMapper.updateBookInformationInBookTable(sapBmsBook);
        //修改借阅表
        bmsMapper.updateBorrowStateByOperator(bmsBorrowVo);

        //获得最新的信息
        borrowInfo = bmsMapper.getBorrowInfoByBmsBorrowId(bmsBorrowVo.getBmsBorrowId());
        UserMessage managerMessage = userMapper.getUserMessage(borrowInfo.getBmsBorrowOperator());
        UserMessage userMessage = userMapper.getUserMessage(borrowInfo.getBmsBorrowUserName());

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
        String nowTime = simpleDateFormat.format(new Date());
        if ("已归还".equals(borrowInfo.getBmsBorrowState())) {
            // 已归还 的 状态
            String contentToApplicant = EmailUntil.sendEmailToUserForBookReturnPass(
                    borrowInfo.getBmsBorrowUserName(), borrowInfo.getRealName(), borrowInfo.getBmsBookName(),
                    simpleDateFormat.format(borrowInfo.getBmsBorrowApplicationTime()), simpleDateFormat.format(borrowInfo.getBmsBorrowExpectedReturnTime()),
                    simpleDateFormat.format(borrowInfo.getBmsBorrowActualReturnTime()), borrowInfo.getBmsBorrowOperator(),
                    borrowInfo.getBmsBorrowOperatorName(), managerMessage.getQq());
            iMailService.sendHtmlMail(userMessage.getQq() + "@qq.com", "书籍归还成功", contentToApplicant);
            String notice = "您归还《" + borrowInfo.getBmsBookName() + "》的审批已被管理员" + managerMessage.getRealName() + "审核";
            userService.sendNoticeAuto(new NoticeDto(userMessage.getUsername(), notice, null));
        } else {
            // 损坏 或 遗失 的状态
            String contentToApplicant = EmailUntil.sendEmailToUserForBookPay(borrowInfo.getBmsBorrowUserName(), borrowInfo.getRealName(), borrowInfo.getBmsBookName(),
                    simpleDateFormat.format(borrowInfo.getBmsBorrowApplicationTime()), nowTime, bmsMapper.getBookInfo(borrowInfo.getBmsBookId()).getBmsBookPrice(),
                    borrowInfo.getBmsBorrowOperator(), borrowInfo.getBmsBorrowOperatorName(), managerMessage.getQq());
            iMailService.sendHtmlMail(userMessage.getQq() + "@qq.com", "书籍损坏或遗失赔偿", contentToApplicant);
            String notice = "您赔偿《" + borrowInfo.getBmsBookName() + "》的审批已被管理员" + managerMessage.getRealName() + "审核";
            userService.sendNoticeAuto(new NoticeDto(userMessage.getUsername(), notice, null));
        }
        String contentToOtherManagers = "已归还".equals(borrowInfo.getBmsBorrowState()) ? EmailUntil.sendEmailToOtherManagerForBookReturnPass(
                borrowInfo.getBmsBorrowUserName(), borrowInfo.getRealName(), borrowInfo.getBmsBookName(),
                simpleDateFormat.format(borrowInfo.getBmsBorrowApplicationTime()), simpleDateFormat.format(borrowInfo.getBmsBorrowExpectedReturnTime()),
                simpleDateFormat.format(borrowInfo.getBmsBorrowActualReturnTime()), borrowInfo.getBmsBorrowOperator(),
                borrowInfo.getBmsBorrowOperatorName()) : EmailUntil.sendEmailToOtherManagerForBookPay(borrowInfo.getBmsBorrowUserName(), borrowInfo.getRealName(), borrowInfo.getBmsBookName(),
                simpleDateFormat.format(borrowInfo.getBmsBorrowApplicationTime()), nowTime,
                bmsMapper.getBookInfo(borrowInfo.getBmsBookId()).getBmsBookPrice(),
                borrowInfo.getBmsBorrowOperator(), borrowInfo.getBmsBorrowOperatorName());
        List<SapBmsManagerSimpleInfo> sapBmsManagerSimpleInfos = bmsMapper.getSapBmsManagerSimpleInfos();
        String subject = "已归还".equals(borrowInfo.getBmsBorrowState()) ? "一条书籍归还消息已被其他管理员审核" : "一条书籍损坏或遗失消息已被其他管理员审核";
        for (SapBmsManagerSimpleInfo sapBmsManagerSimpleInfo : sapBmsManagerSimpleInfos) {
            if (!sapBmsManagerSimpleInfo.getManagerUsername().equals(borrowInfo.getBmsBorrowOperator())) {
                iMailService.sendHtmlMail(sapBmsManagerSimpleInfo.getManagerQq() + "@qq.com", subject, contentToOtherManagers);
                String notice = "来自" + userMessage.getUsername() + " " +
                        userMessage.getRealName() + borrowInfo.getBmsBorrowState() + "《" + borrowInfo.getBmsBookName() + "》的审核已被管理员" + managerMessage.getRealName() + "处理，请勿重复操作";
                userService.sendNoticeAuto(new NoticeDto(sapBmsManagerSimpleInfo.getManagerUsername(), notice, null));
            }
        }

        return new SimpleJson(0, "审批成功");
    }

    @Override
    public List<BmsCategoryWithBookNumberVo> getCategoryWithBookNumber() {
        return bmsMapper.getCategoryWithBookNumber();
    }

    @Override
    public SimpleJson cancelBorrowOrReturnApplication(Integer bmsBorrowId, int type) {
        if (type != 0 && type != 1) {
            return null;
        }
        SapBmsBorrow borrowApplication = bmsMapper.getBorrowById(bmsBorrowId);
        if (borrowApplication == null) {
            return new SimpleJson(2, "编号不存在");
        }
        //操作人与记录申请人不匹配
        String username = JwtUtil.getUserForToken();
        if (!username.equals(borrowApplication.getBmsBorrowUserName())) {
            return new SimpleJson(3, "无法操作别人的记录");
        }

        //检测该申请是否被处理或是其他状态 、 修改状态 以及 邮件和通知的生成
        String name = cacheService.getRealNameCache(cacheService.getRealNameCacheMap(), username);
        Integer bookId = borrowApplication.getBmsBookId();
        SapBmsBook book = bmsMapper.getBookInfo(bookId);
        String subject, email, notice;
        if (type == 0) {
            //取消借阅申请
            if ("借阅中".equals(borrowApplication.getBmsBorrowState())) {
                return new SimpleJson(4, "借阅请求已被审核通过，如不需要可直接申请归还");
            } else if ("申请失败".equals(borrowApplication.getBmsBorrowState())) {
                return new SimpleJson(5, "借阅请求已驳回，不需要再取消");
            } else if (!"申请中".equals(borrowApplication.getBmsBorrowState())) {
                return new SimpleJson(6, "不能操作流程已结束的记录");
            }
            //修改状态
            bmsMapper.updateBorrowState(bmsBorrowId, "已取消");
            //生成通知和邮件
            subject = "取消借阅提醒";
            email = EmailUntil.emailForCancelBorrow(username, name,
                    book.getBmsBookName(), bookId);
            notice = "[取消借阅]\n  学号为" + username + "的同学:" +
                    name + "在" + new Timestamp(System.currentTimeMillis()) +
                    "取消了针对 编号为" + bookId + "的《" + book.getBmsBookName() + "》的借阅请求";
        } else {
            //取消归还申请
            if ("已归还".equals(borrowApplication.getBmsBorrowState())) {
                return new SimpleJson(4, "归还申请已被审核通过，可联系管理员更改");
            } else if (!"申请归还".equals(borrowApplication.getBmsBorrowState())) {
                return new SimpleJson(5, "不能操作其他状态的记录");
            }
            //修改状态
            bmsMapper.updateBorrowState(bmsBorrowId, "借阅中");
            //生成通知和邮件
            subject = "取消归还提醒";
            email = EmailUntil.emailForCancelReturn(username, name,
                    book.getBmsBookName(), bookId);
            notice = "[取消归还]\n  学号为" + username + "的同学:" +
                    name + "在" + new Timestamp(System.currentTimeMillis()) +
                    "取消了针对 编号为" + bookId + "的《" + book.getBmsBookName() + "》的归还请求";
        }

        //发送邮件和通知
        List<BmsManagerVo> responsibleManagerVos = bmsMapper.getBmsResponsibleManagerVos();
        if (sendEmailAndNoticeToResponsibleManagers(responsibleManagerVos, notice, subject, email)) {
            //全部发送失败，尝试向会长发邮件提醒，返回操作成功但向管理员发送邮件提醒失败
            trySendEmailToPresidentUnderAbnormalConditions("进行取消图书借阅/归还申请操作时", responsibleManagerVos.size());
            return new SimpleJson(1, "操作成功，但向管理员发送邮件提醒失败");
        }
        return SimpleJson.SUCCESS;
    }

    /**
     * 工具方法 -- 向所有正式管理员发送邮件和通知
     *
     * @param responsibleManagerVos 正式管理员列表 (当前负责管理的管理层人员)
     * @param notice                通知
     * @param subject               邮件题目
     * @param email                 邮件内容
     * @return 是否全部发送失败
     */
    private boolean sendEmailAndNoticeToResponsibleManagers(List<BmsManagerVo> responsibleManagerVos, String notice, String subject, String email) {
        //邮件发送失败提醒
        int sendFalseCnt = 0;
        for (BmsManagerVo managerVo : responsibleManagerVos) {
            try {
                userService.sendNotice(new NoticeDto(managerVo.getUsername(), notice, null));
                iMailService.sendHtmlMail(managerVo.getQq() + "@qq.com", subject, email);
            } catch (RuntimeException e) {
                sendFalseCnt++;
            }
        }
        return sendFalseCnt == responsibleManagerVos.size();
    }

    /**
     * 工具方法 -- 当向正式管理员发送邮件全部失败时，尝试向会长发邮件提醒
     *
     * @param tips                     调用什么接口时失败 (格式 ： 在进行***操作时)
     * @param responsibleManagerNumber 当前正式管理员数量
     */
    private void trySendEmailToPresidentUnderAbnormalConditions(String tips, int responsibleManagerNumber) {
        User president = userMapper.getPresidentUser();
        try {
            userService.sendNotice(new NoticeDto(president.getUsername(), tips + "邮件服务异常", null));
            iMailService.sendSimpleMail(president.getQq() + "@qq.com", "邮件服务异常", tips + "向所有正式管理员发送的邮件均发送失败! 当前正式管理员人数:" + responsibleManagerNumber);
        } catch (RuntimeException e) {
            logger.error("发送邮件时发生异常！", e);
        }
    }

    @Override
    public List<BorrowNumberInEachMonth> getBorrowNumberEachMonthInThePastYear() {
        /*1. 获取过去一年中每个月的借阅申请次数数据 (只包含有数据的)*/
        List<BorrowNumberInEachMonth> borrowNumberEachMonth = bmsMapper.getBorrowNumberEachMonthInThePastYear();

        /*2. 获取当前年月*/
        DateFormat fmt = new SimpleDateFormat("yyyy-MM");
        String[] split = fmt.format(new Date()).split("-");
        int nowYear = Integer.parseInt(split[0]);
        int nowMonth = Integer.parseInt(split[1]);

        /*3. 倒推填充每个月的数据，没有查询到数据的月份从redis取值，redis没有的情况下随机生成一个1-3的数字并填充到redis中 (当前月份和寒暑假没数据直接填0)*/
        List<BorrowNumberInEachMonth> result = new ArrayList<>();
        //查询到的列表的索引 (查询结果从近到远排列，而年月数据填充时也是从当前年月开始，所以当索引对应的数据中的data不等于倒退到的年月，就说明对应年月的申请借阅次数为0，需要按要求填充一个值)
        int idx = 0;
        int year = nowYear;
        //月份值+1是因为每次循环时要-1，减少一个if(i!=0)为true才执行年月变化的判断(就算nowMonth=12使month=13，在处理时因为首先进行了-1时也会使第一个month=12,不会有影响)
        int month = nowMonth + 1;
        //redis缓存
        Map<String, Object> cacheMap = redisCache.getCacheMap(ConstantUntil.SAP_BMS_BORROW_NUMBER_EACH_MONTH);
        //倒推处理每月数据，i=0时为当前年月
        for (int i = 0; i < 12; ++i) {
            //年月变化
            if (month - 1 == 0) {
                month = 12;
                --year;
            } else {
                --month;
            }
            //查找是否有对应年月的数据
            String data = month < 10 ? year + "-0" + month : year + "-" + month;
            if (idx < borrowNumberEachMonth.size() && data.equals(borrowNumberEachMonth.get(idx).getDate())) {
                result.add(borrowNumberEachMonth.get(idx++));
            } else {
                //sql查询获得的列表没有该月的数据 -> 该月借阅申请数=0 -> 按要求赋值
                //当前实际月份和寒暑假(按1.2.7.8月处理)没数据直接填0
                if (year == nowYear && month == nowMonth) {
                    result.add(new BorrowNumberInEachMonth(data, 0));
                } else if (month == 1 || month == 2 || month == 7 || month == 8) {
                    result.add(new BorrowNumberInEachMonth(data, 0));
                }
                //其他月份利用redis存取一个1-3的值
                else {
                    if (cacheMap.containsKey(data)) {
                        //能查找到直接就取
                        result.add(new BorrowNumberInEachMonth(data, (Integer) cacheMap.get(data)));
                    } else {
                        //不能查找到就先生成
                        //翻转cacheMap,防止某个值出现次数过高
                        Map<Object, ArrayList<String>> collect = cacheMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue,
                                entry -> new ArrayList<String>() {{
                                    add(entry.getKey());
                                }},
                                (key1, key2) -> {
                                    key1.addAll(key2);
                                    return key1;
                                }));

                        Integer[] arr = new Integer[]{1, 2, 3};
                        Arrays.sort(arr, Comparator.comparingInt(a -> collect.containsKey(a) ? collect.get(a).size() : 0));
                        int number = arr[new Random().nextInt(2)];

                        cacheMap.put(data, number);
                        redisCache.setCacheMap(ConstantUntil.SAP_BMS_BORROW_NUMBER_EACH_MONTH, cacheMap);
                        result.add(new BorrowNumberInEachMonth(data, number));
                    }
                }
            }
        }
        return result;
    }
}
